﻿using MahApps.Metro.Controls;
using System.Windows;
using System.Windows.Input;
using System;
using System.Windows.Media;
using System.Resources;
using System.Windows.Shapes;
using System.Windows.Controls;

namespace WinDrone
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : MetroWindow
    {
        #region [ Private Enumeration Definitions ]

        /// <summary>
        /// Flight states enumeration of the drone.
        /// </summary>
        private enum FlightState
        {
            Landed = 0,
            Flying = 1
        }

        /// <summary>
        /// Emegency modes enumeration of the drone.
        /// </summary>
        private enum EmergencyMode
        {
            NormalState = 0,
            EmergentState = 1
        }

        #endregion


        #region [Private Constant Configurations and Resources]

        /// <summary>
        /// Button icon of emergent state in emergency mode.
        /// </summary>
        private static VisualBrush _VISUALBRUSH_EMERGENCY_EMERGENT;

        /// <summary>
        /// Button icon of normal state in emergency mode.
        /// </summary>
        private static VisualBrush _VISUALBRUSH_EMERGENCY_NORMAL;

        /// <summary>
        /// Button icon of landing in flight state.
        /// </summary>
        private static VisualBrush _VISUALBRUSH_FLIGHT_LAND;

        /// <summary>
        /// Button icon of taking-off in flight state.
        /// </summary>
        private static VisualBrush _VISUALBRUSH_FLIGHT_TAKEOFF;

        /// <summary>
        /// Transmission interval for AT command. (Unit: ms)
        /// </summary>
        private const double _AT_COMMAND_INTERVAL = 15.0;

        #endregion


        #region [ Private Member Variables ]

        /// <summary>
        /// Flight state of the drone.
        /// </summary>
        private FlightState _CurrentFlightState = FlightState.Landed;

        /// <summary>
        /// Emergency mode of the drone.
        /// </summary>
        private EmergencyMode _CurrentEmergencyMode = EmergencyMode.NormalState;

        /// <summary>
        /// Timer for counting when to send an AT command
        /// </summary>
        private System.Timers.Timer _ATCommandSenderTimer = null;

        /// <summary>
        /// Indicate if take-off command has been sent in AT command sender handler.
        /// </summary>
        private bool _SentTakeOffCommand = false;

        /// <summary>
        /// Activation of "spin right" action.
        /// </summary>
        private bool _PinSpinRight = false;

        /// <summary>
        /// Activation of "spin left" action.
        /// </summary>
        private bool _PinSpinLeft = false;

        /// <summary>
        /// Activation of "move front" action.
        /// </summary>
        private bool _PinMoveFront = false;

        /// <summary>
        /// Activation of "move back" action.
        /// </summary>
        private bool _PinMoveBack = false;

        /// <summary>
        /// Activation of "move left" action.
        /// </summary>
        private bool _PinMoveLeft = false;

        /// <summary>
        /// Activation of "move right" action.
        /// </summary>
        private bool _PinMoveRight = false;

        /// <summary>
        /// Activation of "altitude lift" action.
        /// </summary>
        private bool _PinAltitudeLift = false;

        /// <summary>
        /// Activation of "altitude descent" action.
        /// </summary>
        private bool _PinAltitudeDescent = false;

        #endregion


        #region [ Private Member Functions ]

        /// <summary>
        /// Handler function called for each AT command sender clock.
        /// </summary>
        private void _ATCommandSenderHandler()
        {
            if ((_CurrentEmergencyMode == EmergencyMode.NormalState) && (_CurrentFlightState == FlightState.Flying))
            {
                if (_SentTakeOffCommand)
                {
                    if (_PinSpinLeft || _PinSpinRight || _PinMoveFront || _PinMoveBack || _PinMoveLeft || _PinMoveRight || _PinAltitudeLift || _PinAltitudeDescent)
                    {
                        ARDrone2.Action.Move.AdvancedProgress(
                            true,
                            _PinMoveLeft ? -0.3f : (_PinMoveRight ? 0.3f : 0),
                            _PinMoveFront ? -0.3f : (_PinMoveBack ? 0.3f : 0),
                            _PinAltitudeLift ? 0.3f : (_PinAltitudeDescent ? -0.3f : 0),
                            _PinSpinLeft ? -0.3f : (_PinSpinRight ? 0.3f : 0)
                            );
                    }
                    else
                    {
                        // No action activated, then hover
                        ARDrone2.Action.Move.Hover();
                    }
                }
                else
                {
                    // Send take-off command
                    ARDrone2.Action.TakeOff();
                    _SentTakeOffCommand = true;
                }
            }
            else
            {
                // Always keep the drone on the ground
                ARDrone2.Action.Land();

                // Reset the take-off command sent state
                _SentTakeOffCommand = false;
            }
        }

        /// <summary>
        /// Switch current flight state of the drone.
        /// </summary>
        private void _SwitchFlightState()
        {
            if (_CurrentEmergencyMode == EmergencyMode.NormalState)
            {
                // Change the flight state
                switch (_CurrentFlightState)
                {
                    case FlightState.Landed:
                        // Take off the drone
                        _CurrentFlightState = FlightState.Flying;
                        btnSwitchFlightState_Icon.Fill = _VISUALBRUSH_FLIGHT_LAND;

                        // Activate the control panel
                        btnYawClockwise.IsEnabled = true;
                        btnYawAnticlockwise.IsEnabled = true;
                        btnMoveFront.IsEnabled = true;
                        btnMoveBack.IsEnabled = true;
                        btnMoveLeft.IsEnabled = true;
                        btnMoveRight.IsEnabled = true;
                        btnAltitudeDescent.IsEnabled = true;
                        btnAltitudeLift.IsEnabled = true;
                        break;

                    case FlightState.Flying:
                        // Land the drone
                        _CurrentFlightState = FlightState.Landed;
                        btnSwitchFlightState_Icon.Fill = _VISUALBRUSH_FLIGHT_TAKEOFF;

                        // Lock the control panel
                        btnYawClockwise.IsEnabled = false;
                        btnYawAnticlockwise.IsEnabled = false;
                        btnMoveFront.IsEnabled = false;
                        btnMoveBack.IsEnabled = false;
                        btnMoveLeft.IsEnabled = false;
                        btnMoveRight.IsEnabled = false;
                        btnAltitudeDescent.IsEnabled = false;
                        btnAltitudeLift.IsEnabled = false;
                        break;
                }
            }
        }

        /// <summary>
        /// Switch current emergency mode of the drone.
        /// </summary>
        private void _SwitchEmergencyMode()
        {
            // Change the emergency mode
            switch (_CurrentEmergencyMode)
            {
                case EmergencyMode.NormalState:
                    if (MessageBox.Show("Shotdown All Engines?", "WARNING!", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
                    {
                        // Switch to emergent state
                        _CurrentEmergencyMode = EmergencyMode.EmergentState;
                        ARDrone2.Action.SwitchEmergencyMode();
                        btnSwitchEmergencyMode_Icon.Fill = _VISUALBRUSH_EMERGENCY_NORMAL;

                        // Land the drone
                        _CurrentFlightState = FlightState.Landed;
                        btnSwitchFlightState_Icon.Fill = _VISUALBRUSH_FLIGHT_TAKEOFF;

                        // Lock the control panel
                        btnSwitchFlightState.IsEnabled = false;
                        btnYawClockwise.IsEnabled = false;
                        btnYawAnticlockwise.IsEnabled = false;
                        btnMoveFront.IsEnabled = false;
                        btnMoveBack.IsEnabled = false;
                        btnMoveLeft.IsEnabled = false;
                        btnMoveRight.IsEnabled = false;
                        btnAltitudeDescent.IsEnabled = false;
                        btnAltitudeLift.IsEnabled = false;
                    }
                    break;
                case EmergencyMode.EmergentState:
                    if (MessageBox.Show("Resume Normal State?", "WARNING!", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
                    {
                        // Switch to normal state
                        _CurrentEmergencyMode = EmergencyMode.NormalState;
                        ARDrone2.Action.SwitchEmergencyMode();
                        btnSwitchEmergencyMode_Icon.Fill = _VISUALBRUSH_EMERGENCY_EMERGENT;

                        // Resume the take-off button
                        btnSwitchFlightState.IsEnabled = true;
                    }
                    break;
            }
        }

        #endregion


        /// <summary>
        /// Construct a new MainWindows class object.
        /// </summary>
        public MainWindow()
        {
            // Load the button icon resources
            _VISUALBRUSH_EMERGENCY_EMERGENT = new VisualBrush((Canvas)FindResource("emergency"));
            _VISUALBRUSH_EMERGENCY_NORMAL = new VisualBrush((Canvas)FindResource("play"));
            _VISUALBRUSH_FLIGHT_LAND = new VisualBrush((Canvas)FindResource("land"));
            _VISUALBRUSH_FLIGHT_TAKEOFF = new VisualBrush((Canvas)FindResource("take_off"));

            // Initialize the window controls
            InitializeComponent();

            // Initialize the flight state as landed
            _CurrentFlightState = FlightState.Landed;

            // Initialize the emergency mode as normal state
            _CurrentEmergencyMode = EmergencyMode.NormalState;

            // Initialize the take-off command sent state
            _SentTakeOffCommand = false;

            // Initialize the actions
            _PinAltitudeDescent = false;
            _PinAltitudeLift = false;
            _PinMoveBack = false;
            _PinMoveFront = false;
            _PinMoveLeft = false;
            _PinMoveRight = false;
            _PinSpinLeft = false;
            _PinSpinRight = false;

            // Initialize the AT command sender timer
            _ATCommandSenderTimer = new System.Timers.Timer(_AT_COMMAND_INTERVAL);
            _ATCommandSenderTimer.Elapsed += new System.Timers.ElapsedEventHandler((obj, e) => { _ATCommandSenderHandler(); });
            _ATCommandSenderTimer.Enabled = true;
        }


        // EVENT: MainWindow closed
        private void MainWindow_Closed(object sender, EventArgs e)
        {
            Application.Current.Shutdown();
        }

        // EVENT: Press any key
        private void MainWindow_KeyDown(object sender, KeyEventArgs e)
        {
            e.Handled = true;
            switch (e.Key)
            {
                case Key.Q: _PinSpinLeft = true; break;
                case Key.E: _PinSpinRight = true; break;
                case Key.W: _PinMoveFront = true; break;
                case Key.S: _PinMoveBack = true; break;
                case Key.A: _PinMoveLeft = true; break;
                case Key.D: _PinMoveRight = true; break;
                case Key.Up: _PinMoveFront = true; break;
                case Key.Down: _PinMoveBack = true; break;
                case Key.Left: _PinMoveLeft = true; break;
                case Key.Right: _PinMoveRight = true; break;
                case Key.OemPlus: _PinAltitudeLift = true; break;
                case Key.OemMinus: _PinAltitudeDescent = true; break;
                case Key.Add: _PinAltitudeLift = true; break;
                case Key.Subtract: _PinAltitudeDescent = true; break;
                default: e.Handled = false; break;
            }
        }

        // EVENT: Release any key
        private void MainWindow_KeyUp(object sender, KeyEventArgs e)
        {
            e.Handled = true;
            switch (e.Key)
            {
                case Key.Space: _SwitchFlightState(); break; // Press "Space" key to switch the flight state
                case Key.R: _SwitchEmergencyMode(); break; // Press "R" key to switch the emergency mode
                case Key.Q: _PinSpinLeft = false; break;
                case Key.E: _PinSpinRight = false; break;
                case Key.W: _PinMoveFront = false; break;
                case Key.S: _PinMoveBack = false; break;
                case Key.A: _PinMoveLeft = false; break;
                case Key.D: _PinMoveRight = false; break;
                case Key.Up: _PinMoveFront = false; break;
                case Key.Down: _PinMoveBack = false; break;
                case Key.Left: _PinMoveLeft = false; break;
                case Key.Right: _PinMoveRight = false; break;
                case Key.OemPlus: _PinAltitudeLift = false; break;
                case Key.OemMinus: _PinAltitudeDescent = false; break;
                case Key.Add: _PinAltitudeLift = false; break;
                case Key.Subtract: _PinAltitudeDescent = false; break;
                default: e.Handled = false; break;
            }
        }

        // EVENT: Click the "Switch Flight State" button
        private void btnSwitchFlightState_Click(object sender, RoutedEventArgs e)
        {
            _SwitchFlightState();
        }

        // EVENT: Click the "Switch Emergency Mode" button
        private void btnSwitchEmergencyMode_Click(object sender, RoutedEventArgs e)
        {
            _SwitchEmergencyMode();
        }

        // EVENT: Press down the "Yaw Clockwise" button
        private void btnYawClockwise_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Pin corresponding action
            _PinSpinRight = true;
        }

        // EVENT: Release the "Yaw Clockwise" button
        private void btnYawClockwise_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Unpin corresponding action
            _PinSpinRight = false;
        }

        // EVENT: Press down the "Yaw Anticlockwise" button
        private void btnYawAnticlockwise_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Pin corresponding action
            _PinSpinLeft = true;
        }

        // EVENT: Release the "Yaw Anticlockwise" button
        private void btnYawAnticlockwise_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Unpin corresponding action
            _PinSpinLeft = false;
        }

        // EVENT: Press the "Move Front" button
        private void btnMoveFront_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Pin corresponding action
            _PinMoveFront = true;
        }

        // EVENT: Release the "Move Front" button
        private void btnMoveFront_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Unpin corresponding action
            _PinMoveFront = false;
        }

        // EVENT: Press down the "Move Back" button
        private void btnMoveBack_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Pin corresponding action
            _PinMoveBack = true;
        }

        // EVENT: Release the "Move Back" button
        private void btnMoveBack_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Unpin corresponding action
            _PinMoveBack = false;
        }

        // EVENT: Press down the "Move Left" button
        private void btnMoveLeft_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Pin corresponding action
            _PinMoveLeft = true;
        }

        // EVENT: Release the "Move Left" button
        private void btnMoveLeft_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Unpin corresponding action
            _PinMoveLeft = false;
        }

        // EVENT: Press down the "Move Right" button
        private void btnMoveRight_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Pin corresponding action
            _PinMoveRight = true;
        }

        // EVENT: Release the "Move Right" button
        private void btnMoveRight_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Unpin corresponding action
            _PinMoveRight = false;
        }

        // EVENT: Press down the "Altitude Lift" button
        private void btnAltitudeLift_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Pin corresponding action
            _PinAltitudeLift = true;
        }

        // EVENT: Release the "Altitude Lift" button
        private void btnAltitudeLift_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Unpin corresponding action
            _PinAltitudeLift = false;
        }

        // EVENT: Press down the "Altitude Descent" button
        private void btnAltitudeDescent_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // Pin corresponding action
            _PinAltitudeDescent = true;
        }

        // EVENT: Release the "Altitude Descent" button
        private void btnAltitudeDescent_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // Unpin corresponding action
            _PinAltitudeDescent = false;
        }
    }
}
